package com.keba.basic.database.plugins;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.keba.basic.encrypt.annotation.Encrypted;
import com.keba.basic.encrypt.annotation.Security;
import com.keba.basic.encrypt.handler.CommonSecurityHandler;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

import static org.apache.ibatis.mapping.SqlCommandType.*;


/**
 * 字段加解密控制 拦截器
 * <p>
 *
 * @author twg
 * @date 2025/4/15
 */
@SuppressWarnings("AlibabaUndefineMagicConstant")
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class EncryptInterceptor implements Interceptor {

    @Resource
    private CommonSecurityHandler commonSecurityHandler;


    @Override
    @SneakyThrows
    public Object intercept(Invocation invocation) {
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        if (SELECT.equals(sqlCommandType) || DELETE.equals(sqlCommandType)) {
            return invocation.proceed();
        }
        Class<?> clazz = mappedStatement.getParameterMap().getType();
        if (Objects.nonNull(clazz) && clazz.isAnnotationPresent(Encrypted.class)) {
            Field[] fields = ReflectUtil.getFields(clazz);
            for (Field field : fields) {
                if (field.isAnnotationPresent(Security.class) && String.class.isAssignableFrom(field.getType())) {
                    BoundSql boundSql = statementHandler.getBoundSql();
                    Object parameterObject = boundSql.getParameterObject();
                    if (parameterObject instanceof Map) {
                        Map<String, Object> parameterMap = (Map<String, Object>) parameterObject;
                        if (parameterMap.containsKey("et") && Objects.nonNull(parameterMap.get("et"))) {
                            Object obj = parameterMap.get("et");
                            Object object = ReflectUtil.getFieldValue(obj, field);
                            if (Objects.isNull(object) || StrUtil.isBlank(object.toString())) {
                                continue;
                            }
                            ReflectUtil.setFieldValue(obj, field, commonSecurityHandler.handleEncrypt(object.toString()));
                        }
                    } else {
                        Object object = ReflectUtil.getFieldValue(parameterObject, field);
                        if (Objects.nonNull(object)) {
                            String fieldValue = object.toString();
                            if (StrUtil.isBlank(fieldValue)) {
                                continue;
                            }
                            ReflectUtil.setFieldValue(parameterObject, field, commonSecurityHandler.handleEncrypt(fieldValue));
                        }

                    }

                }
            }
        }
        return invocation.proceed();
    }

    /**
     * 生成拦截对象的代理
     *
     * @param target 目标对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * mybatis配置的属性
     *
     * @param properties mybatis配置的属性
     */
    @Override
    public void setProperties(Properties properties) {

    }

}
